#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <pfs.h>
#include <dybuf.h>
#include <strhelper.h>
#include <endianhelper.h>

#include <vector>
#include <algorithm>

#include "common.h"

// purpose:
// make a dictionary, the dictionary contain only
// the word name. it can used in case of word complete
// input file is a text file, word stored line by line
// ex:
//   hello
//   kitty
//   long
//   short
// it's allow white space.

// the file header like this
// struct Header {
//   int32 id;
//   int32 version;
//   int32 word_length;
// };
//
// and words follow...

struct Word {
    char               *ptr;

    bool operator< (const Word& word) const
    {
        return strcmp(ptr, word.ptr) < 0;
    }

    Word() : ptr(NULL)
    {
    }

    Word(const char *str)
    {
        int len = strlen(str);

        ptr = (char *) malloc (len + 1);

        memcpy(ptr, str, len);

        ptr[len] = 0;
    }
};

typedef std::vector<Word> Words;

static char *
_get_nl(char *buf, int size, char **npos)
{
    int i;

    for (i = 0; i < size; i++) {
        switch (buf[i]) {
        case '\n':
            *npos = buf + i + 1;
            return buf + i;
            break;

        case '\r':
            // check appending \n
            if (i + 1 < size && buf[i + 1] == '\n')
                *npos = buf + i + 2;
            else
                *npos = buf + i + 1;
            return buf + i;
            break;
        }
    }

    return NULL;
}

static int
_load_words_from_file(const Path& path, Words& words)
{
    DyBuf *dybuf = pfs_load_file(path);

    if (dybuf == NULL)
        return -1;

    for (;;) {
        char *buf = dybuf->get_buf();
        int   size = dybuf->get_size();

        if (!buf || size <= 0)
            break;

        char *end;

        char *nl = _get_nl(buf, size, &end);

        std::string word;

        if (nl) {
            word.assign(buf, nl - buf);
            dybuf->drop_first(end - buf);
        }
        else {
            word.assign(buf);
            dybuf->clear();
        }

        str_lrtrim(word);

        if (word.size() > 0 && word.size() + 1 < MAX_WORD_LENGTH)
            words.push_back(Word(word.c_str()));
    }

    delete dybuf;

    std::sort(words.begin(), words.end());

    return words.size();
}

static int
_store_words_to_file(const Path& path, const Words& words)
{
    file_handle_type handle =
        pfs_open(path, (POM_READWRITE | POM_TRUNC
                        | POM_CREAT), 0644);

    if (!PFS_VALID_HANDLE_P(handle))
        return -1;

    char ib[4];
    int  i;
    int  s;
    char word[MAX_WORD_LENGTH];

    endian_itom_le(DICT_IDENT, ib);
    if (pfs_write(handle, ib, 4) != 4)
        goto failed;

    endian_itom_le(DICT_VERSION, ib);
    if (pfs_write(handle, ib, 4) != 4)
        goto failed;

    endian_itom_le(MAX_WORD_LENGTH, ib);
    if (pfs_write(handle, ib, 4) != 4)
        goto failed;

    s = words.size();

    for (i = 0; i < s; i++) {
        memset(word, 0, MAX_WORD_LENGTH);

        strcpy(word, words[i].ptr);

        if (pfs_write(handle, word, MAX_WORD_LENGTH) != MAX_WORD_LENGTH)
            goto failed;
    }

    pfs_close(handle);
    return 0;

failed:
    pfs_close(handle);
    return -1;
}

int main(int argc, char *argv[])
{
    if (argc != 3) {
        fprintf(stderr, "%s input output\n", argv[0]);
        exit(1);
    }

    Path  inpath(argv[1]);
    Path  outpath(argv[2]);
    Words words;

    if (_load_words_from_file(inpath, words) > 0) {
        printf("%d words loaded\n", words.size());

        if (_store_words_to_file(outpath, words) == 0)
            printf("store to file done\n");
        else
            fprintf(stderr, "store to file failed: %s\n", strerror(errno));
    }
    else
        fprintf(stderr, "load nothing\n");

    return 0;
}
